﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace SvnMigration
{
    public class SvnProcessHelper
    {
        private static string Quote(string stringToBeQuoted)
        {
            return String.Format(@"""{0}""", stringToBeQuoted);
        }

        public static IEnumerable<CommitInfo> GetSvnLog(SvnOptions svnOptions)
        {
            StringBuilder argBuilder = new StringBuilder();
            argBuilder.Append(" log");
            argBuilder.Append(" --xml");
            string resultString = AddLoginInfoAndRunSvnProcess(svnOptions, argBuilder, null, true);

            XmlDocument xml = new XmlDocument();
            xml.LoadXml(resultString);

            List<CommitInfo> result = new List<CommitInfo>();

            foreach (XmlNode node in xml.SelectNodes("/log/logentry"))
            {
                int revision = Convert.ToInt32(node.Attributes["revision"].InnerText);
                XmlNode messageNode = node.SelectSingleNode("msg");
                string message = messageNode != null ? messageNode.InnerText : null;
                XmlNode authorNode = node.SelectSingleNode("author");
                string author = authorNode != null ? authorNode.InnerText : null;
                CommitInfo commitInfo = new CommitInfo(revision, message, author);
                result.Add(commitInfo);
            }
            result.Reverse();
            return result;
        }

        public static string AddLoginInfoAndRunSvnProcess(SvnOptions svnOptions, StringBuilder argBuilder, string path, bool needUrl)
        {
            if (needUrl)
            {
                argBuilder.Append(" " + Quote(svnOptions.Url));
            }
            if (path != null)
            {
                argBuilder.Append(" " + path);
            }
            argBuilder.Append(" --username " + svnOptions.Username);
            argBuilder.Append(" --password " + svnOptions.Password);
            argBuilder.Append(" --non-interactive");
            argBuilder.Append(" --no-auth-cache");

            ProcessStartInfo info = new ProcessStartInfo("svn.exe", argBuilder.ToString());
            info.UseShellExecute = false;
            info.ErrorDialog = false;
            info.CreateNoWindow = true;
            info.RedirectStandardOutput = true;

            Process process = new Process();
            process.StartInfo = info;
            process.Start();
            string result = process.StandardOutput.ReadToEnd();
            process.WaitForExit();
            return result;
        }

        public static void Checkout(SvnOptions svnOptions)
        {
            StringBuilder argBuilder = new StringBuilder();
            argBuilder.Append(" checkout");
            AddLoginInfoAndRunSvnProcess(svnOptions, argBuilder, "target", true);
        }

        public static void Export(SvnOptions svnOptions, CommitInfo info)
        {
            StringBuilder argBuilder = new StringBuilder();
            argBuilder.Append(" export");
            argBuilder.Append(" --force");
            argBuilder.Append(" -r " + info.Revision);
            AddLoginInfoAndRunSvnProcess(svnOptions, argBuilder, "target", true);
        }

        public static void Commit(SvnOptions svnOptions, CommitInfo info)
        {
            StringBuilder argBuilder = new StringBuilder();
            argBuilder.Append(" commit");
            argBuilder.Append(" -m " + Quote(info.Author + ":" + " " + info.Message));
            AddLoginInfoAndRunSvnProcess(svnOptions, argBuilder, "target", false);
        }

        public static void Add(SvnOptions svnOptions, string targetPath)
        {
            foreach (string directoryName in Directory.GetDirectories(targetPath).Where(item => !item.EndsWith(".svn")))
            {
                AddPath(svnOptions, directoryName);
                Add(svnOptions, directoryName);
            }
            string[] files = Directory.GetFiles(targetPath);
            foreach (var file in files)
            {
                AddPath(svnOptions, file);
            }
        }

        private static void AddPath(SvnOptions svnOptions, string path)
        {
            StringBuilder argBuilder = new StringBuilder();
            argBuilder.Append(" add");
            argBuilder.Append(" " + path);
            AddLoginInfoAndRunSvnProcess(svnOptions, argBuilder, null, false);
        }
    }
}
